package com.ian.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;

@RestController
public class ServletAPIController {

    @GetMapping("/hello")
    public String hello() {
        System.out.println("hello");
        return "hello";
    }

    @GetMapping("/hello1")
    public String hello1() {
        System.out.println("hello1");
        return "hello1";
    }

    @GetMapping("/hello2")
    public String hello2() {
        System.out.println("hello2");
        return "hello2";
    }

    @GetMapping("/user")
    public String user() {
        System.out.println("user");
        return "user";
    }

    @GetMapping("/admin")
    public String admin() {
        System.out.println("admin");
        return "admin";
    }

    // Servlet 2.5+ Integration
    /**
     * return the currently logged in user
     * @return the currently logged in user
     */
    @GetMapping("/currUsername")
    public String currUsername(HttpServletRequest request) {
        System.out.println("currUsername");
        String currUsernameFromRequest = request.getRemoteUser();
        String currUsernameFromSS = SecurityContextHolder.getContext().getAuthentication().getName();
        System.out.println("currUsernameFromRequest == currUsernameFromSS : " + (currUsernameFromRequest == currUsernameFromSS));
        System.out.println("currUsernameFromRequest.equals(currUsernameFromSS) : " + currUsernameFromRequest.equals(currUsernameFromSS));
//        return currUsernameFromRequest;
        return currUsernameFromSS;
    }

    @GetMapping("/currUserPrincipal")
    public String currUserPrincipal(HttpServletRequest request) {
        System.out.println("currUserPrincipal");
        Principal currUserPrincipalFromRequest = request.getUserPrincipal();
        Authentication currUserPrincipalFromSS = SecurityContextHolder.getContext().getAuthentication();
        System.out.println("currUserPrincipalFromRequest == currUserPrincipalFromSS : " + (currUserPrincipalFromRequest == currUserPrincipalFromSS));
        System.out.println("currUserPrincipalFromRequest.equals(currUserPrincipalFromSS) : " + currUserPrincipalFromRequest.equals(currUserPrincipalFromSS));

        /*
         assume integrated custom UserDetails called MyCustomUserDetails by default, typically instance of UserDetails
         MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
         String firstName = userDetails.getFirstName();
         String lastName = userDetails.getLastName();
         */
//        return currUsernameFromRequest;
        return currUserPrincipalFromSS.toString();
    }

    /**
     * The HttpServletRequest.isUserInRole(String) will determine if SecurityContextHolder.getContext().getAuthentication().getAuthorities()
     * contains a GrantedAuthority with the role passed into isUserInRole(String).
     * Typically users should not pass in the "ROLE_" prefix into this method since it is added automatically.
     * @param request
     * @return
     */
    @GetMapping("/isUserInRole")
    public String isUserInRole(HttpServletRequest request, HttpServletResponse response) {
        boolean isAdmin = request.isUserInRole("admin");
        return "isAdmin: " + isAdmin;
    }

    // Servlet 3+ Integration

    /**
     * The HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse) method can be used to ensure that a user is authenticated. If they are not authenticated, the configured AuthenticationEntryPoint will be used to request the user to authenticate (i.e. redirect to the login page)
     * @param request
     * @param response
     * @return
     */
    @GetMapping("/isAuthenticated")
    public String authenticate(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        boolean isAuthenticated = request.authenticate(response);
        return "isAuthenticated: " + isAuthenticated;
    }

    /**
     * The HttpServletRequest.login(String,String) method can be used to authenticate the user with the current AuthenticationManager. For example, the following would attempt to authenticate with the username "user" and password "password":
     * @param request
     * @return
     */
    @GetMapping("/requestLogin")
    public String requestLogin(HttpServletRequest request) {
        try {
//        request.login("user", "password"); // javax.servlet.ServletException: Cannot perform login for 'user' already authenticated as 'user'
//        request.login("user2", "password"); // javax.servlet.ServletException: Cannot perform login for 'user2' already authenticated as 'user'
            request.login("admin", "123"); // javax.servlet.ServletException: Cannot perform login for 'admin' already authenticated as 'user'
        } catch(ServletException ex) {
            // fail to authenticate
            ex.printStackTrace();
        }
        return "requestLogin";
    }

    /**
     * The HttpServletRequest.logout() method can be used to log the current user out.
     *
     * Typically this means that the SecurityContextHolder will be cleared out, the HttpSession will be invalidated, any "Remember Me" authentication will be cleaned up, etc. However, the configured LogoutHandler implementations will vary depending on your Spring Security configuration. It is important to note that after HttpServletRequest.logout() has been invoked, you are still in charge of writing a response out. Typically this would involve a redirect to the welcome page.
     * @param request
     * @return
     */
    @GetMapping("/requestLogout")
    public String requestLogout(HttpServletRequest request) throws ServletException {
        request.logout();
        return "requestLogout";
    }

    /**
     * The AsyncContext.start(Runnable) method that ensures your credentials will be propagated to the new Thread. Using Spring Security’s concurrency support, Spring Security overrides the AsyncContext.start(Runnable) to ensure that the current SecurityContext is used when processing the Runnable. For example, the following would output the current user’s Authentication:
     * @param request
     */
    @GetMapping("/asyncContextStart")
    public void asyncContextStart(HttpServletRequest request) {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.start(() -> {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            try {
                final HttpServletResponse asyncResponse = (HttpServletResponse) asyncContext.getResponse();
                asyncResponse.setStatus(HttpServletResponse.SC_OK);
                asyncResponse.getWriter().write(String.valueOf(authentication)); // error output: java.lang.IllegalStateException: getWriter() has already been called for this response
                asyncContext.complete();
            } catch (Exception e) {
//                e.printStackTrace();
                throw new RuntimeException(e);
            }
        });
//        return "asyncContextStart"; // 不一定要 “return "String values"”，方法返回值也可设置为 void，只要调用 response.getWriter().write() 方法，也可以返回数据到前端页面
    }

    /*
     Servlet 3.1+ Integration
     The HttpServletRequest.changeSessionId() is the default method for protecting against Session Fixation attacks in Servlet 3.1 and higher.
     */
}
